package org.yunai.swjg.server.module.inventory;

import org.slf4j.Logger;
import org.yunai.swjg.server.core.constants.SysMessageConstants;
import org.yunai.swjg.server.module.inventory.container.Bag;
import org.yunai.swjg.server.module.inventory.container.CommonBag;
import org.yunai.swjg.server.module.inventory.container.PlayerEquipBag;
import org.yunai.swjg.server.module.inventory.container.ShoulderBag;
import org.yunai.swjg.server.module.inventory.item.Item;
import org.yunai.swjg.server.module.inventory.item.template.ItemTemplate;
import org.yunai.swjg.server.module.player.vo.Player;
import org.yunai.swjg.server.rpc.message.S_C.S_C_ItemGetResp;
import org.yunai.swjg.server.rpc.message.S_C.S_C_SysMessageReq;
import org.yunai.swjg.server.rpc.struct.StItemGet;
import org.yunai.yfserver.common.LoggerFactory;

import java.util.*;

/**
 * 背包清单
 * User: yunai
 * Date: 13-4-6
 * Time: 下午3:04
 */
public class Inventory {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerFactory.Logger.item, Inventory.class);

    /**
     * 默认背包大小
     */
    public static final int DEFAULT_CAPACITY = 6;

    /**
     * 玩家
     */
    private Player player;
    /**
     * 玩家装备背包
     */
    private final PlayerEquipBag playerEquipBag;
    /**
     * 玩家仓库背包
     */
    private final ShoulderBag depotBag;
    /**
     * 玩家主背包
     */
    private final ShoulderBag primBag;
    /**
     * 背包集合
     */
    private final Map<Bag.BagType, CommonBag> bags;

    public Inventory(Player player) {
        this.player = player;

        this.bags = new HashMap<>(3);
        this.playerEquipBag = new PlayerEquipBag(player, Bag.BagType.PLAYER_EQUIP);
        this.depotBag = new ShoulderBag(player, Bag.BagType.DEPOT, DEFAULT_CAPACITY);
        this.primBag = new ShoulderBag(player, Bag.BagType.PRIM, DEFAULT_CAPACITY);
        this.bags.put(this.playerEquipBag.getBagType(), this.playerEquipBag);
        this.bags.put(this.depotBag.getBagType(), this.depotBag);
        this.bags.put(this.primBag.getBagType(), this.primBag);
    }

    public void putItem(Item item) {
        CommonBag bag = bags.get(item.getBagType());
        if (bag != null) {
            // TODO check道具
            bag.putItem(item);
        } else {
            // TODO log
        }
    }

    public PlayerEquipBag getPlayerEquipBag() {
        return playerEquipBag;
    }

    public ShoulderBag getPrimBag() {
        return primBag;
    }

    public CommonBag getBag(Bag.BagType bagType) {
        return bags.get(bagType);
    }

    @SuppressWarnings("unchecked")
    public <T> T getBag(Bag.BagType bagType, Class<T> clazz) {
        return (T) this.getBag(bagType);
    }

    /**
     * 交换两个背包的位置
     *
     * @param itemA 道具A
     * @param itemB 道具B
     */
    public void swap(Item itemA, Item itemB) {
        // 验证
        if (itemA == itemB) {
            return;
        }
        // 交换
        int indexA = itemA.getIndex();
        if (itemA.getBagType() != itemB.getBagType()) {
            Bag.BagType bagTypeA = itemA.getBagType();
            itemA.changeBagType(itemB.getBagType(), itemB.getIndex());
            itemB.changeBagType(bagTypeA, indexA);
            this.getBag(itemA.getBagType()).putItem(itemA);
            this.getBag(itemB.getBagType()).putItem(itemB);
        } else {
            itemA.changeIndex(itemB.getIndex());
            itemB.changeIndex(indexA);
        }
    }

    /**
     * 检查主背包空间是否足够
     *
     * @param itemParams 道具列表
     * @param showError  是否提示消息给客户端
     * @return 是否足够
     */
    public boolean checkPrimBagSpace(Collection<ItemParam> itemParams, boolean showError) {
        ShoulderBag primBag = getPrimBag();
        for (ItemParam itemParam : itemParams) {
            ItemTemplate template = ItemTemplate.getItemTemplate(itemParam.getTemplateId());
            if (primBag.getMaxCanAdd(template) < itemParam.getCount()) {
                if (showError) {
                    player.getOnline().write(new S_C_SysMessageReq(SysMessageConstants.BAG_PRIM_NOT_ENOUGH));
                }
                return false;
            }
        }
        return true;
    }

    /**
     * 添加道具到玩家主背包<br />
     * <pre>
     *     1. 道具获得消息给客户端
     *     2. 发送道具变化列表消息给客户端
     * </pre>
     * 另外该方法，不会检查空间是否足够。所以，调用该方法前，请调用{@link #checkPrimBagSpace(java.util.Collection, boolean)}
     *
     * @param itemParams 道具参数列表
     */
    public void addPrimBagItems(Collection<ItemParam> itemParams) {
        // 去除Collection里道具数量为不合法的
        Iterator<ItemParam> iterator = itemParams.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().getCount() <= 0) {
                iterator.remove();
            }
        }
        if (itemParams.isEmpty()) {
            return;
        }
        // 道具添加
        List<Item> changedItems = new ArrayList<>(itemParams.size());
        ShoulderBag primBag = getPrimBag();
        for (ItemParam itemParam : itemParams) {
            ItemTemplate template = ItemTemplate.getItemTemplate(itemParam.getTemplateId());
            List<Item> subChangedItems = primBag.add(template, itemParam.getCount());
            if (subChangedItems.isEmpty()) { // 该处若添加道具没产生道具变化，则认为是错误的
                LOGGER.error("[addPrimBagItems] [player({}) add item({}) * num({}) error.]", player.getId(), itemParam.getTemplateId(), itemParam.getCount());
                throw new IllegalStateException("player( " + player.getId() + ") add item(" + itemParam.getTemplateId() + ") * num(" + itemParam.getCount() + ") error.");
            }
            changedItems.addAll(subChangedItems);
        }
        // 提示道具获得
        List<StItemGet> itemGets = new ArrayList<>(itemParams.size());
        for (ItemParam itemParam : itemParams) {
            itemGets.add(new StItemGet(itemParam.getTemplateId(), itemParam.getCount()));
        }
        player.getOnline().write(new S_C_ItemGetResp(itemGets));
        // 提示道具变化
        player.getOnline().write(BagMessageBuilder.build(changedItems));
    }

    /**
     * 检查主背包道具是否足够
     *
     * @param itemParams 道具列表
     * @param showError  是否提示消息给客户端
     * @return 是否足够
     */
    public boolean checkPrimBagEnough(Collection<ItemParam> itemParams, boolean showError) {
        ShoulderBag primBag = getPrimBag();
        for (ItemParam itemParam : itemParams) {
            if (primBag.getCountByTemplateId(itemParam.getTemplateId()) < itemParam.getCount()) {
                if (showError) {
                    player.getOnline().write(new S_C_SysMessageReq(SysMessageConstants.BAG_PRIM_ITEM_NOT_ENOUGH));
                }
                return false;
            }
        }
        return true;
    }

    /**
     * 从玩家主背包减少道具<br />
     * <pre>
     *     1. 发送道具变化列表消息给客户端
     * </pre>
     * 另外该方法，不会道具数量是否足够。所以，调用该方法前，请调用{@link #checkPrimBagEnough(java.util.Collection, boolean)}
     *
     * @param itemParams 道具参数列表
     */
    public void reducePrimBagItems(Collection<ItemParam> itemParams) {
        // 去除Collection里道具数量为不合法的
        Iterator<ItemParam> iterator = itemParams.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().getCount() <= 0) {
                iterator.remove();
            }
        }
        if (itemParams.isEmpty()) {
            return;
        }
        // 道具减少
        List<Item> changedItems = new ArrayList<>(itemParams.size());
        ShoulderBag primBag = getPrimBag();
        for (ItemParam itemParam : itemParams) {
            ItemTemplate template = ItemTemplate.getItemTemplate(itemParam.getTemplateId());
            List<Item> subChangedItems = primBag.reduce(template, itemParam.getCount());
            if (subChangedItems.isEmpty()) { // 该处若添加道具没产生道具变化，则认为是错误的
                LOGGER.error("[reducePrimBagItems] [player({}) reduce item({}) * num({}) error.]", player.getId(), itemParam.getTemplateId(), itemParam.getCount());
                throw new IllegalStateException("player( " + player.getId() + ") reduce item(" + itemParam.getTemplateId() + ") * num(" + itemParam.getCount() + ") error.");
            }
            changedItems.addAll(subChangedItems);
        }
        // 提示道具变化
        player.getOnline().write(BagMessageBuilder.build(changedItems));
    }
}
